package com.cjfather.redis.steam.config;

import java.time.Duration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ForkJoinPool;

import io.lettuce.core.ClientOptions;
import io.lettuce.core.SocketOptions;
import io.lettuce.core.protocol.ProtocolVersion;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.cjfather.redis.steam.annotation.serializer.FurySerializer;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;

/**
 * 
 * @author: todd
 * @date: 2023-08-08
 */

@Configuration
@Slf4j
public class CommonConfig {

	@Bean(name = "forkJoinPool")
	public ExecutorService forkJoinPool() {
		return new ForkJoinPool();
	}

	@Bean(name = "threadPoolTaskExecutor")
	public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
		return new ThreadPoolTaskExecutor();
	}
	
	@Value("${spring.redis.host}")
	private String hostName;

	@Value("${spring.redis.password}")
	private String passWord;

	@Value("${spring.redis.port}")
	private int port;

	@Value("${spring.redis.database:0}")
	private int database;

	@Value("${spring.redis.timeout:10000}")
	private int timeOut;

	@Value("${spring.redis.lettuce.pool.max-active}")
	private int maxActive;

	@Value("${spring.redis.lettuce.pool.max-idle}")
	private int maxIdle;

	@Value("${spring.redis.lettuce.pool.min-idle}")
	private int minIdle;

	@Value("${spring.redis.lettuce.pool.max-wait}")
	private int maxWait;

	@Value("${spring.redis.lettuce.command-timeout}")
	private int commandTimeout;

	@Value("${spring.redis.lettuce.shutdown-timeout:1000}")
	private int shutdownTimeOut;

	@Value("${spring.redis.client-type:lettuce}")
	private String clientType;
	
	@Bean
	public RedisConnectionFactory redisConnectionFactory() {
		RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
		config.setHostName(hostName);
		config.setPort(port);
		config.setDatabase(database);
		config.setPassword(RedisPassword.of(passWord));

		//连接池配置
		GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
		genericObjectPoolConfig.setMaxIdle(maxIdle);
		genericObjectPoolConfig.setMinIdle(minIdle);
		genericObjectPoolConfig.setMaxTotal(maxActive);
		genericObjectPoolConfig.setMaxWait(Duration.ofSeconds(-1));

		//redis客户端配置
		LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder =  LettucePoolingClientConfiguration.builder()
						.commandTimeout(Duration.ofMillis(commandTimeout))
						.shutdownTimeout(Duration.ofMillis(shutdownTimeOut))
						.poolConfig(genericObjectPoolConfig);

		// 关键：配置 Lettuce 以正确处理 DMCDM 认证
		LettuceClientConfiguration clientConfig = builder
						.clientOptions(ClientOptions.builder()
						.autoReconnect(true)
						.pingBeforeActivateConnection(false)  // 禁用 ping
						.protocolVersion("lettuce".equals(clientType)?ProtocolVersion.RESP3:ProtocolVersion.RESP2)  // 强制使用 RESP2
						.disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
						.socketOptions(SocketOptions.builder()
								.connectTimeout(Duration.ofSeconds(5))
								.keepAlive(true)
								.tcpNoDelay(true)
								.build())
						.build())
				.build();

		LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(config, clientConfig);
		connectionFactory.afterPropertiesSet();

		log.info("=== DMCDM Lettuce 连接配置 ===");
		log.info("地址: {}:{}", hostName, port);
		log.info("数据库: {}", database);
		log.info("协议: {}", clientType);
		log.info("=============================");

		return connectionFactory;
	}

	@Bean
	public ReactiveRedisConnectionFactory reactiveRedisConnectionFactory() {
		return (LettuceConnectionFactory)this.redisConnectionFactory();
	}

	/**
	 * 响应式 Redis 模板
	 */
	@Bean
	public ReactiveRedisTemplate<String, String> reactiveRedisTemplate() {
		RedisSerializationContext<String, String> serializationContext =
				RedisSerializationContext.<String, String>newSerializationContext()
						.key(StringRedisSerializer.UTF_8)
						.value(StringRedisSerializer.UTF_8)
						.hashKey(StringRedisSerializer.UTF_8)
						.hashValue(StringRedisSerializer.UTF_8)
						.build();

		return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory(), serializationContext);
	}

	/**
	 * 通用对象序列化的响应式模板
	 */
	@Bean
	public ReactiveRedisTemplate<String, Object> reactiveRedisObjectTemplate() {
		Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);

		RedisSerializationContext<String, Object> serializationContext =
				RedisSerializationContext.<String, Object>newSerializationContext()
						.key(StringRedisSerializer.UTF_8)
						.value(serializer)
						.hashKey(StringRedisSerializer.UTF_8)
						.hashValue(serializer)
						.build();

		return new ReactiveRedisTemplate<>(reactiveRedisConnectionFactory(), serializationContext);
	}

	@Bean("redisMq6Factory")
	public StringRedisTemplate redisTemplate() {
		// 设置序列化
		Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, Visibility.ANY);
		om.enableDefaultTyping(DefaultTyping.NON_FINAL);
		jackson2JsonRedisSerializer.setObjectMapper(om);
		// 配置redisTemplate
		StringRedisTemplate redisTemplate = new StringRedisTemplate();
		redisTemplate.setConnectionFactory(redisConnectionFactory());
		RedisSerializer<?> stringSerializer = new StringRedisSerializer();
		redisTemplate.setKeySerializer(stringSerializer);// key序列化
		redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化
		
		redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化
		redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化
		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}
	
	@Bean("redisObjFactory")
	public RedisTemplate<?, ?> redisObjFactory() {
		// 配置redisTemplate
		RedisTemplate<?, ?> redisTemplate = new RedisTemplate();
		redisTemplate.setConnectionFactory(redisConnectionFactory());
		redisTemplate.setKeySerializer(RedisSerializer.string());// key序列化
		redisTemplate.setHashKeySerializer(RedisSerializer.string());// Hash key序列化
		
		redisTemplate.setValueSerializer(RedisSerializer.json());// value序列化
		redisTemplate.setHashValueSerializer(RedisSerializer.json());// Hash value序列化
		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}
	
	@Bean("redisFuryFactory")
	public RedisTemplate<?, ?> redisFuryFactory() {
		// 配置redisTemplate
		FurySerializer serializer = new FurySerializer();
		RedisTemplate<?, ?> redisTemplate = new RedisTemplate();
		redisTemplate.setConnectionFactory(redisConnectionFactory());
		
		redisTemplate.setKeySerializer(RedisSerializer.string());// key序列化
		redisTemplate.setHashKeySerializer(RedisSerializer.string());// Hash key序列化
		
		redisTemplate.setValueSerializer(RedisSerializer.json());// value序列化
		redisTemplate.setHashValueSerializer(RedisSerializer.json());// Hash value序列化
		
		redisTemplate.afterPropertiesSet();
		return redisTemplate;
	}

}